﻿using Common.DataAnnotations;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace Common
{
    /// <summary>
    /// 扩展方法
    /// </summary>
    public static class ExtensionFunctions
    {
        /// <summary>
        /// 转换为以属性分行的字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToLineText(this object obj)
        {
            Type t = obj.GetType();
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(t.FullName);
            var properties = t.GetProperties();
            foreach (var item in properties)
            {
                sb.AppendLine($"[{item.Name.PadRight(20, (char)0x20)}]{item.GetValue(obj, null)}");
            }
            return sb.ToString();
        }
        /// <summary>
        /// 将对象转换成字典
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="dictionary"></param>
        /// <param name="beforeAdding"></param>
        /// <param name="afterAdded"></param>
        /// <returns></returns>
        public static IDictionary<string, object> ToDictionary(this object obj, IDictionary<string, object> dictionary = null,
            Func<NameValue<string, object>, NameValue<string, object>> beforeAdding = null,
            Action<IDictionary<string, object>, NameValue<string, object>> afterAdded = null)
        {
            if (dictionary == null)
            {
                dictionary = new Dictionary<string, object>();
            }
            if (obj == null)
            {
                return dictionary;
            }
            var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var p in properties)
            {
                NameValue<string, object> nvp = NameValue.Instance(p.Name, p.GetValue(obj));
                if (beforeAdding != null)
                {
                    nvp = beforeAdding.Invoke(nvp);
                }
                if (nvp != null)
                {
                    dictionary.Add(nvp.Name, nvp.Value);
                }
                afterAdded?.Invoke(dictionary, nvp);
            }
            return dictionary;
        }
        /// <summary>
        /// 保存HTML中的远程图片
        /// </summary>
        /// <param name="html">需要处理的HTML源串</param>
        /// <param name="savedLocalPaths">保存至本地的路径集合</param>
        /// <param name="localUri">本地Uri</param>
        /// <param name="saveDirectory">文件保存目录</param>
        /// <returns></returns>
        public static string SaveRemoteImage(this string html, Uri localUri, string saveDirectory, out List<string> savedLocalPaths)
        {
            const string imagePattern = @"<\s*img.*?[/]?>";
            const string srcPattern = "(.+?src\\s*=[\"'])(.+?)([\"\'].+)";
            savedLocalPaths = new List<string>();
            List<string> imgList = new List<string>();
            var mc = Regex.Matches(html, imagePattern);
            if (mc.Count == 0)
            {
                return html;
            }

            if (saveDirectory.StartsWith("/") || saveDirectory.StartsWith("\\"))
            {
                saveDirectory = saveDirectory.Substring(1, saveDirectory.Length - 1);
            }
            //完整目录名称
            string directoryName = saveDirectory;
            if (!Regex.IsMatch(saveDirectory, @"[a-zA-Z]:\\"))
            {
                directoryName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, saveDirectory);
            }
            //如果目录不存在则创建
            if (!System.IO.Directory.Exists(directoryName))
            {
                System.IO.Directory.CreateDirectory(directoryName);
            }
            foreach (Match match in mc)
            {
                imgList.Add(match.Value);
            }
            foreach (string imageSytax in imgList)
            {
                var match = Regex.Match(imageSytax, srcPattern);
                string url = match.Groups[2].Value;
                if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                {
                    Uri uri = new Uri(url);
                    //如果图片域名与需要剔除的域名相同，直接进入下一次循环
                    if (uri.Host.Equals(localUri.Host, StringComparison.OrdinalIgnoreCase))
                    {
                        savedLocalPaths.Add(url);
                        continue;
                    }
                    string extName = ".jpg";
                    if (System.IO.Path.HasExtension(uri.AbsoluteUri))
                    {
                        extName = System.IO.Path.GetExtension(uri.AbsoluteUri);
                        if (extName.IndexOf('?') >= 0)
                        {
                            extName = extName.Substring(0, extName.IndexOf('?'));
                        }
                    }

                    //guid文件名
                    string filename = "auto_" + Guid.NewGuid().ToString("N") + extName;
                    //完整路径，包含盘符
                    string fullpath = System.IO.Path.Combine(directoryName, filename);
                    //调用方传入的相对路径
                    string localpath = System.IO.Path.Combine(saveDirectory, filename);

                    savedLocalPaths.Add("/" + localpath);
                    WebClient wc = new WebClient();
                    wc.DownloadFile(uri, fullpath);
                    wc.Dispose();
                    string replacedSyntax = Regex.Replace(imageSytax, srcPattern, "$1" + "/" + localpath + "$3");
                    html = html.Replace(imageSytax, replacedSyntax);
                }
                else
                {
                    savedLocalPaths.Add(url);
                }
            }
            return html;
        }
        /// <summary>
        /// 将集合的某个字段合并为一个字符串
        /// </summary>
        /// <typeparam name="T">集合类型</typeparam>
        /// <param name="self">源集合</param>
        /// <param name="express">表达式（it=>it.Name）</param>
        /// <param name="seperator">分隔符</param>
        /// <returns></returns>
        public static string Join<T>(this IEnumerable<T> self, Expression<Func<T, object>> express, char seperator)
        {
            var a = self.Select(express.Compile());
            return string.Join(new string(seperator, 1), a);
        }

        /// <summary>
        /// 截断指定小数位，20180522解决溢出风险
        /// </summary>
        /// <param name="d"></param>
        /// <param name="scale">指定保留小数位数</param>
        /// <returns></returns>
        public static decimal TruncateValue(this decimal d, int scale)
        {
            decimal block = (decimal)Math.Pow(10, scale);
            decimal zs = decimal.Truncate(d);//得到整数部分
            decimal xs = d - zs;//得到小数部分
            decimal val = decimal.Truncate(xs * block) / block;//小数部分进行位数保留
            return val + zs;//最终结果，小数部分保留位数+整数部分
        }
        /// <summary>
        /// 冒泡排序法,按照字母序列从a到z的顺序排列
        /// </summary>
        /// <param name="r"></param>
        /// <returns></returns>
        public static ArrayList BubbleSort(this ArrayList r)
        {
            int i, j; //交换标志 
            object temp;

            bool exchange;

            for (i = 0; i < r.Count; i++) //最多做R.Length-1趟排序 
            {
                exchange = false; //本趟排序开始前，交换标志应为假

                for (j = r.Count - 2; j >= i; j--)
                {//交换条件
                    if (System.String.CompareOrdinal(r[j + 1].ToString(), r[j].ToString()) < 0)
                    {
                        temp = r[j + 1];
                        r[j + 1] = r[j];
                        r[j] = temp;

                        exchange = true; //发生了交换，故将交换标志置为真 
                    }
                }
                if (!exchange) //本趟排序未发生交换，提前终止算法 
                {
                    break;
                }
            }
            return r;
        }

        #region Mapper 扩展
        /// <summary>
        /// 这个对象映射扩展不完善，无法处理复杂对象，请勿使用
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        [Obsolete("这个对象映射扩展不完善，无法处理复杂对象，请勿使用")]
        public static void MapTo<TSource, TTarget>(this TSource source, TTarget target) where TSource : class where TTarget : class, new()
        {
            Mapper.Map(source, target);
        }
        /// <summary>
        /// 这个对象映射扩展不完善，无法处理复杂对象，请勿使用
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="sources"></param>
        /// <param name="target"></param>
        /// <param name="action"></param>
        [Obsolete("这个对象映射扩展不完善，无法处理复杂对象，请勿使用")]
        public static void MapListTo<TSource, TResult>(this IEnumerable<TSource> sources, IEnumerable<TResult> target, Action<TSource, TResult> action = null) where TSource : class where TResult : class, new()
        {
            var result = Mapper.MapList(sources, action);
            result.ForEach(item => target.Append(item));
        }
        #endregion
        #region hex to bytes and bytes to hex
        /// <summary>
        /// 16进制字符串转字节数组
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static byte[] HexString2ByteArray(this string s)
        {
            int len = s.Length;
            if (len % 2 != 0)
            {
                s += " ";
                len += 1;
            }
            int blen = len / 2;
            byte[] bytes = new byte[blen];
            for (int i = 0; i < blen; i++)
            {
                bytes[i] = Convert.ToByte(s.Substring(i * 2, 2), 16);
            }
            return bytes;
        }
        /// <summary>
        /// 字节数组转换16进制字符串
        /// </summary>
        /// <param name="bs"></param>
        /// <returns></returns>
        public static string ByteArray2HexString(this byte[] bs)
        {
            StringBuilder hex = new StringBuilder();
            foreach (byte b in bs)
                hex.Append(b.ToString("x").PadLeft(2, '0'));
            return hex.ToString();
        }
        #endregion
        #region xml operation
        /// <summary>
        /// 取得xml节点值
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string ElementValue(this XElement ele, string name)
        {
            if (ele == null)
                return string.Empty;
            XElement xe = ele.Element(name);
            if (xe == null)
                return string.Empty;
            return xe.Value;
        }
        /// <summary>
        /// 取得xml节点值
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="name"></param>
        /// <param name="defaultvalue"></param>
        /// <returns></returns>
        public static string ElementValue(this XElement ele, string name, string defaultvalue)
        {
            if (ele == null)
                return defaultvalue;
            XElement xe = ele.Element(name);
            if (xe == null)
                return defaultvalue;
            return xe.Value;
        }
        /// <summary>
        /// 取得xml节点值
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="name"></param>
        /// <param name="defaultvalue"></param>
        /// <returns></returns>
        public static decimal ElementValue(this XElement ele, string name, decimal defaultvalue)
        {
            return Convert.ToDecimal(ElementValue(ele, name, defaultvalue.ToString()));
        }
        /// <summary>
        /// 取得xml节点值
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="name"></param>
        /// <param name="defaultvalue"></param>
        /// <returns></returns>
        public static int ElementValue(this XElement ele, string name, int defaultvalue)
        {
            return Convert.ToInt32(ElementValue(ele, name, defaultvalue.ToString()));
        }
        /// <summary>
        /// 取得xml节点值
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="name"></param>
        /// <param name="defaultvalue"></param>
        /// <returns></returns>
        public static bool ElementValue(this XElement ele, string name, bool defaultvalue)
        {
            string value = ele.ElementValue(name);
            if (string.IsNullOrEmpty(value))
                return defaultvalue;
            bool result = false;
            switch (value.ToLower())
            {
                case "false": result = false; break;
                case "true": result = true; break;
                default:
                    int i = 0;
                    if (!int.TryParse(value, out i))
                        result = false;
                    else
                        result = i > 0;
                    break;
            }
            return result;
        }
        #endregion

        #region Prompt transform to FuncResult
        /// <summary>
        /// Prompt转换为表示失败的FuncResult
        /// </summary>
        /// <param name="prompt"></param>
        /// <returns></returns>
        [Obsolete("please use IPromptInfo.ToFailResult() instead")]
        public static FuncResult ToFailResult(this PromptInfo prompt)
        {
            int val = (int)prompt.ResultType;
            string code = val == 0 ? FuncResult._FAIL_CODE : val.ToString().PadLeft(4, '0');
            return FuncResult.Fail(prompt.CustomMessage, code);
        }
        /// <summary>
        /// Prompt转换为表示失败的FuncResult
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prompt"></param>
        /// <returns></returns>
        [Obsolete("please use IPromptInfo.ToFailResult<T>() instead")]
        public static FuncResult<T> ToFailResult<T>(this PromptInfo prompt)
        {
            int val = (int)prompt.ResultType;
            string code = val == 0 ? FuncResult._FAIL_CODE : val.ToString().PadLeft(4, '0');
            return FuncResult.Fail<T>(prompt.CustomMessage, code);
        }
        /// <summary>
        /// Prompt转换为表示失败的FuncResult
        /// </summary>
        /// <param name="prompt"></param>
        /// <returns></returns>
        public static FuncResult ToFailResult(this IPromptInfo prompt)
        {
            int val = (int)prompt.ResultType;
            string code = val == 0 ? FuncResult._FAIL_CODE : val.ToString().PadLeft(4, '0');
            return FuncResult.Fail(prompt.CustomMessage, code);
        }
        /// <summary>
        /// Prompt转换为表示失败的FuncResult
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prompt"></param>
        /// <returns></returns>
        public static FuncResult<T> ToFailResult<T>(this IPromptInfo prompt)
        {
            int val = (int)prompt.ResultType;
            string code = val == 0 ? FuncResult._FAIL_CODE : val.ToString().PadLeft(4, '0');
            return FuncResult.Fail<T>(prompt.CustomMessage, code);
        }

        /// <summary>
        /// Prompt转换为表示失败的FuncResult
        /// </summary>
        /// <param name="prompt"></param>
        /// <param name="defaultMessage">默认错误消息</param>
        /// <returns></returns>
        public static FuncResult ToFailResult(this IPromptInfo prompt, string defaultMessage)
        {
            int val = (int)prompt.ResultType;
            string code = val == 0 ? FuncResult._FAIL_CODE : val.ToString().PadLeft(4, '0');
            return FuncResult.Fail(prompt.CustomMessage ?? defaultMessage, code);
        }
        /// <summary>
        /// Prompt转换为表示失败的FuncResult
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prompt"></param>
        /// <param name="defaultMessage">默认错误消息</param>
        /// <returns></returns>
        public static FuncResult<T> ToFailResult<T>(this IPromptInfo prompt, string defaultMessage)
        {
            int val = (int)prompt.ResultType;
            string code = val == 0 ? FuncResult._FAIL_CODE : val.ToString().PadLeft(4, '0');
            return FuncResult.Fail<T>(prompt.CustomMessage ?? defaultMessage, code);
        }
        #endregion
        /// <summary>
        /// 循环执行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="array"></param>
        /// <param name="action"></param>
        public static void ForEach<T>(this IEnumerable<T> array, Action<T> action)
        {
            if (array == null || !array.Any())
            {
                return;
            }
            foreach (var item in array)
            {
                action(item);
            }
        }
        /// <summary>
        /// 判断是否为枚举类型（可空枚举类型也视为枚举）
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsEnum(this Type type)
        {
            return IsEnum(type, out _);
        }
        /// <summary>
        /// 判断是否为枚举类型并输出枚举类型（可空枚举类型也视为枚举）
        /// </summary>
        /// <param name="type"></param>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static bool IsEnum(this Type type, out Type enumType)
        {
            if (type.IsEnum)
            {
                enumType = type;
                return true;
            }
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && (enumType = Nullable.GetUnderlyingType(type)).IsEnum)
            {
                return true;
            }
            enumType = null;
            return false;
        }
    }
}
